package com.sniff.atp.utils;

import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson2.JSON;
import com.sniff.atp.constant.CaseConstant;
import com.sniff.atp.dto.ContentDTO;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class HttpClientUtil {
    private final Logger log = LoggerFactory.getLogger(HttpClientUtil.class);
    private Map<String, String> header = new HashMap<>();
    private final Map<String, String> respHeader = new HashMap<>();
    private List<BasicClientCookie> cookies = new ArrayList<>();
    private List<Cookie> respCookies;
    private StatusLine statusLine;
    private Integer status;
    private byte[] bytes;
    private String userName;
    private String password;
    private static volatile CloseableHttpClient httpClient;

    public HttpClientUtil() {
        String grayTag = GrayTagThreadLocal.get();
        if (ObjUtil.isEmpty(grayTag)){
            grayTag = "master";
        }
//        log.info("内置设置请求环境参数,grayTag:{}", grayTag);
        this.header.put("Content-Type", "application/json");
        this.header.put("X-GRAY-TAG", grayTag);
        this.header.put("X-stationCode", "JapanStation");
        this.instance();
    }

    public HttpClientUtil(Map<String, String> header) {
        this.instance();
        String grayTag = GrayTagThreadLocal.get();
        if (ObjUtil.isEmpty(grayTag)){
            grayTag = "master";
        }
//        log.info("内置设置请求环境参数,grayTag:{}", grayTag);
        this.header.put("Content-Type", "application/json");
        this.header.put("X-GRAY-TAG", grayTag);
        this.header.put("X-stationCode", "JapanStation");
        this.header.putAll(header);
    }

    private void instance() {
        if (httpClient == null) {
            synchronized (CloseableHttpClient.class) {
                if (httpClient == null) {
                    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
                    connectionManager.setMaxTotal(600);
                    connectionManager.setDefaultMaxPerRoute(100);
                    httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
                }
            }
        }
    }

    public void setBasicCredentials(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public Integer getStatus() {
        return status;
    }

    public void setCookies(List<BasicClientCookie> cookies) {
        this.cookies = cookies;
    }

    public void setHeader(Map<String, String> header) {
        this.header = header;
    }

    public Map<String, String> getRespHeader() {
        return respHeader;
    }

    public List<Cookie> getRespCookies() {
        return respCookies;
    }

    public StatusLine getStatusLine() {
        return statusLine;
    }

    /**
     * 发送一个POST请求，参数为JSON或者对象
     */
    public String doPostJson(String url, Object params) {
        requestLog(url, params);
        HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(params)) {
            httpPost.setEntity(new StringEntity(toJSONString(params), StandardCharsets.UTF_8));
        }
        return getResultString(httpPost);
    }

    /**
     * 发送一个GET请求，不带参数，或者参数写在URL上
     */
    public String doGetURL(String url) {
        requestLog(url, null);
        HttpGet httpGet = new HttpGet(url);
        return getResultString(httpGet);
    }

    private byte[] doGetBytes(String url) {
        requestLog(url, null);
        HttpGet httpGet = new HttpGet(url);
        return getResultBytes(httpGet);
    }

    /**
     * 发送一个GET请求，URL+参数，参数由当前方法进行组装
     */
    public String doGet(String url, Map<String, String> params) {
        URI uri = paramsToURI(url, params);
        requestLog(uri.toString(), null);
        HttpGet httpGet = new HttpGet(uri);
        return getResultString(httpGet);
    }

    /**
     * 发送一个PUT请求，参数为JSON或者对象
     */
    public String doPutJson(String url, Object params) {
        requestLog(url, params);
        HttpPut httpPut = new HttpPut(url);
        httpPut.setEntity(new StringEntity(toJSONString(params), StandardCharsets.UTF_8));
        return getResultString(httpPut);
    }

    public String doPutFile(String url, String path) {
        requestLog(url, null);
        HttpPut httpPut = new HttpPut(url);
        ByteArrayEntity entity = null;
        try {
            entity = new ByteArrayEntity(FileUtils.readFileToByteArray(new File(path)));
        } catch (IOException e) {
            log.info("文件不存在，: {}", e.getMessage());
        }
        httpPut.setEntity(entity);
        return getResultString(httpPut);
    }

    private String getResultString(HttpRequestBase request) {
        String result = new String(getResultBytes(request), StandardCharsets.UTF_8);
        resultLog(result);
        AssertUtil.assertTrue(result, request.getURI().toString());
        return result;
    }

    private String getResultStringNoAssert(HttpRequestBase request) {
        String result = new String(getResultBytes(request), StandardCharsets.UTF_8);
        resultLog(result);
        return result;
    }

    private byte[] getResultBytes(HttpRequestBase request) {
        HttpClientContext context = setContext();
        if (header != null) {
            addHeader(request);
        }
        request.setConfig(requestConfig());
        try {
            CloseableHttpResponse response = httpClient.execute(request, context);
            HttpEntity resEntity = response.getEntity();
            bytes = EntityUtils.toByteArray(resEntity);
            Header[] headers = response.getAllHeaders();
            for (Header key : headers) {
                respHeader.put(key.getName(), key.getValue());
            }
            statusLine = response.getStatusLine();
            status = statusLine.getStatusCode();
            if (status != 200) {
                LogUtil.error(String.format("状态码： %s", status));
            }
            respCookies = context.getCookieStore().getCookies();
            EntityUtils.consume(resEntity);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            request.releaseConnection();
        }
        return bytes;
    }

    /**
     * 发送一个POST请求，参数格式为FormData
     */
    public String doPost(String url, Map<String, Object> params) {
        this.header.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        requestLog(url, params);
        HttpPost httpPost = new HttpPost(url);
        if (!CollectionUtils.isEmpty(params)) {
            httpPost.setEntity(paramsToEntity(params));
        }
        return getResultString(httpPost);
    }

    /**
     * 发送一个Delete请求，参数格式为FormData，需要token
     */
    public String doDelete(String url, Map<String, String> params) {
        HashMap<String, Object> paramsToen = new HashMap<>();
        paramsToen.put("username", "13221051826");
        paramsToen.put("password", "888888");

        //获取token，先从redis获取，没有登陆获取
        String result = new HttpClientUtil().doPost("https://gateway-prod.theckb.com/customer/users/token", paramsToen);
        String token = JsonUtil.extractString(result, "data.token");

        this.header.put("Content-Type", "application/json");
        this.header.put("x-authtoken", token);

        this.header.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        HttpDelete httpDelete = new HttpDelete(paramsToURI(url, params));
        return getResultString(httpDelete);
    }

    /**
     * 发送一个线上需要token的POST请求，参数格式为json
     */
    public String doPostNeedToken(String url, Map<String, Object> params) {

        HashMap<String, Object> paramsToen = new HashMap<>();
        paramsToen.put("username", "13221051826");
        paramsToen.put("password", "888888");

        //获取token，先从redis获取，没有登陆获取
        String result = new HttpClientUtil().doPost("https://gateway-prod.theckb.com/customer/users/token", paramsToen);
        String token = JsonUtil.extractString(result, "data.token");

        this.header.put("Content-Type", "application/json");
        this.header.put("x-authtoken", token);

        requestLog(url, params);
        HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(params)) {
            httpPost.setEntity(new StringEntity(toJSONString(params), StandardCharsets.UTF_8));
        }
        return getResultStringNoAssert(httpPost);
    }

    /**
     * 发送一个线上需要token的POST请求，参数格式为param
     */
    public String doPostNeedTokenPamrm(String url, Map<String, String> params) {

        HashMap<String, Object> paramsToen = new HashMap<>();
        paramsToen.put("username", "13221051826");
        paramsToen.put("password", "888888");

        //获取token，先从redis获取，没有登陆获取
        String result = new HttpClientUtil().doPost("https://gateway-prod.theckb.com/customer/users/token", paramsToen);
        String token = JsonUtil.extractString(result, "data.token");

        this.header.put("Content-Type", "application/json");
        this.header.put("x-authtoken", token);

        requestLog(url, params);
        HttpPost httpPost = new HttpPost(paramsToURI(url, params));
        return getResultStringNoAssert(httpPost);
    }

    /**
     * 发送一个线上需要token的POST请求，参数格式为json.list的格式
     */
    public String doPostNeedTokenList(String url, Map<String, Object> params) {

        HashMap<String, Object> paramsToen = new HashMap<>();
        paramsToen.put("username", "13221051826");
        paramsToen.put("password", "888888");

        //获取token，先从redis获取，没有登陆获取
//        String result = new HttpClientUtil().doPost("https://gateway-prod.theckb.com/customer/users/token", paramsToen);
//        String token = JsonUtil.extractString(result, "data.token");

        this.header.put("Content-Type", "application/json");
//        this.header.put("x-authtoken", token);
        this.header.put("x-authtoken", "\n" +
                "eyJhbGciOiJIUzI1NiJ9.eyJuaWNrIjoi6YOR5p2-5p2-IiwiZXhwIjoxNzA1OTA4ODk1LCJ1c2VySWQiOiIxNTQyNDEyMTc1ODE2MTMwNTYyIiwidXNlcm5hbWUiOiIxMzIyMTA1MTgyNiJ9.I6uV6n1O0WPhykzOEbqle1oK5iMfib9PaR6ElUXrYpI");

        requestLog(url, params);
        HttpPost httpPost = new HttpPost(url);
        if (Objects.nonNull(params)) {
            httpPost.setEntity(new StringEntity("["+toJSONString(params)+"]", StandardCharsets.UTF_8));
        }
        return getResultStringNoAssert(httpPost);
    }

    private UrlEncodedFormEntity paramsToEntity(Map<String, Object> params) {
        List<NameValuePair> list = new ArrayList<>();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
        }
        return new UrlEncodedFormEntity(list, StandardCharsets.UTF_8);
    }

    private HttpClientContext setContext() {
        CookieStore cookieStore = new BasicCookieStore();
        for (BasicClientCookie cookie : cookies) {
            cookieStore.addCookie(cookie);
        }
        HttpClientContext context = HttpClientContext.create();
        context.setCookieStore(cookieStore);

        return context;
    }

    private void addHeader(HttpRequestBase request) {
        headerLog();
        for (Map.Entry<String, String> entry : header.entrySet()) {
            request.setHeader(entry.getKey(), entry.getValue());
        }
        if (!(StringUtils.isEmpty(userName) || StringUtils.isEmpty(password))) {
            String auth = userName + ":" + password;
            byte[] encodedAuth = Base64.encodeBase64(
                    auth.getBytes(StandardCharsets.UTF_8));
            String authHeader = "Basic " + new String(encodedAuth);
            request.setHeader("Authorization", authHeader);
        }
    }

    private URI paramsToURI(String url, Map<String, String> params) {
        URI uri = null;
        try {
            if (CollectionUtils.isEmpty(params)) {
                uri = new URI(url);
            } else {
                uri = new URI(url + paramsToURL(params));
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return uri;
    }

    private String paramsToURL(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        sb.append("?");
        try {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (entry.getValue() != null) {
                    sb.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name())).append("&");
                }
            }
            sb.deleteCharAt(sb.length() - 1);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    private RequestConfig requestConfig() {
        return RequestConfig.custom().setSocketTimeout(600000).setConnectTimeout(600000).build();
    }

    private String toJSONString(Object params) {
        return JSON.toJSONString(params);
    }

    private Map<String, String> record = new HashMap<>();

    private void requestLog(String url, Object params) {
        if (null != url && !url.isEmpty()) {
            String log = String.format("请求地址: %s", url);
            LogUtil.info(log);
            record.put("reqUrl", url);
        }
        if (null != params) {
            String temp = toJSONString(params);
            if (temp.length() > 50 * 1000) {
                return;
            }
            String log = String.format("请求参数: %s", params);
            LogUtil.info(log);
            record.put("reqBody", temp);
        }
    }

    private void resultLog(String result) {
        String log = String.format("接口返回: %s", result);
        LogUtil.info(log);
        record.put("respHeader", JSON.toJSONString(getRespHeader()));
        record.put("respBody", result);
        record.put("respCode", "" + getStatus());
        if (StringUtils.isEmpty(CaseConstant.caseName)){
            return;
        }
        List<ContentDTO> list = new ArrayList<>();
        list.add(new ContentDTO("caseName", CaseConstant.caseName));
        list.add(new ContentDTO("reqUrl", record.get("reqUrl")));
        list.add(new ContentDTO("reqHeader", record.get("reqHeader")));
        list.add(new ContentDTO("reqBody", record.get("reqBody")));
        list.add(new ContentDTO("respCode", record.get("respCode")));
        list.add(new ContentDTO("respHeader", record.getOrDefault("respHeader", "")));
        list.add(new ContentDTO("respBody", record.getOrDefault("respBody", "")));
        DBUtil.insertOrUpdate("atp-执行记录", list.toArray(new ContentDTO[]{}));
    }

    private void headerLog() {
        String log = String.format("请求头: %s", JSON.toJSONString(header));
        LogUtil.info(log);
        record.put("reqHeader", JSON.toJSONString(header));
    }
}
